#pragma once
#include"Index.hpp"
#include"Util.hpp"
#include <algorithm>
#include<cstdio>
#include <jsoncpp/json/json.h>
#include"log.hpp"
#include <chrono>
#include <fstream>
#include <sstream>

namespace ns_searcher
{
    struct InvertedElemPrint //该结构体用于去重
    {
        uint64_t doc_id;
        int weight;
        std::vector<std::string> words;
        InvertedElemPrint():doc_id(0),weight(0){}
    };

// // 返回当前进程的物理内存占用，单位 KB
// size_t getPeakRSS() {
//     std::ifstream fin("/proc/self/status");
//     std::string line;
//     while (std::getline(fin, line)) {
//         if (line.rfind("VmRSS:", 0) == 0) {          // 找到以 VmRSS 开头的行
//             std::istringstream iss(line);
//             std::string _;
//             size_t kb;
//             iss >> _ >> kb;                         // 提取数值
//             return kb;
//         }
//     }
//     return 0;
// }

    class Searcher
    {
    private:
        ns_index::Index* index;//供系统进行查找的索引
    public:
        Searcher(){}
        ~Searcher(){}
        void InitSearcher(const std::string& input)
        {
            //1.获取/创建index对象
            index=ns_index::Index::GetInstance();
            //std::cout<<"创建单例成功..."<<std::endl;
            LOG(DEBUG,"创建单例成功...");
            
            // 1. 记录起点
            // size_t memBefore = getPeakRSS();
            // auto start = std::chrono::steady_clock::now();

            //2.根据index对象建立索引
            index->BuildIndex(input);
            
            //  // 2. 记录终点
            // auto end = std::chrono::steady_clock::now();
            // size_t memAfter = getPeakRSS();
            // std::cout << "索引构建增量内存: "
            //         << (memAfter - memBefore) / 1024.0
            //         << " MB\n";
            // // 3. 计算差值并输出（毫秒）
            // std::chrono::duration<double, std::milli> elapsed = end - start;
            // std::cout << "耗时: " << elapsed.count() << " ms\n";

            // std::cout<<"建立索引完成..."<<std::endl;
            LOG(DEBUG,"建立索引成功...");
        }
        //query:搜索关键字
        //json_string:返回给用户浏览器的搜索结果
        void Search(const std::string& query,std::string& json_string)
        {
            //1.【分词】：对我们的query进行按照searcher的要求进行分词
            std::vector<std::string> words;
            ns_util::JiebaUtil::CutString(query,&words);

            //2.【触发】：就是根据分词的各个“词”，进行index查找,建立index是忽略大小写，所以搜索，关键字也需要.
            //ns_index::InvertedList inverted_list_all;//内部InvertedElem
            std::vector<InvertedElemPrint> inverted_list_all;

            std::unordered_map<uint64_t,InvertedElemPrint> tokens_map;//去重

            for(std::string word:words)
            {
                boost::to_lower(word);//转化成小写
                ns_index::InvertedList* inverted_list=index->GetInvertedList(word);
                if(nullptr==inverted_list)
                {
                    continue;
                }
                //有一个不太完美的地方：可能出现多个相同的文档
                //eg:雷军/小米->（文档1，文档2，文档1，文档2）
                //inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end());
                for(const auto& elem:*inverted_list)
                {
                    auto& item=tokens_map[elem.doc_id];
                    //item中一定是doc_id相同的print节点
                    item.doc_id=elem.doc_id;
                    item.weight+=elem.weight;
                    item.words.push_back(elem.word);
                }
            }
            for(const auto& item:tokens_map)
            {
                inverted_list_all.push_back(std::move(item.second));//将去重后的结果填写到inverted_list_all中
            }

            //3.【合并触发】：汇总查找结果，按照相关性（weight）降序排序
            // std::sort(inverted_list_all.begin(), inverted_list_all.end(),
            //          [](const ns_index::InvertedElem &e1, const ns_index::InvertedElem &e2){
            //            return e1.weight > e2.weight;
            //            });

            std::sort(inverted_list_all.begin(),inverted_list_all.end(),
                [](const InvertedElemPrint& e1,const InvertedElemPrint& e2){
                    return e1.weight>e2.weight;
                });

            //4.【构建】：根据查找出来的结果，构建json串 -- jsoncpp
            Json::Value root;
            for(auto& item:inverted_list_all)
            {
                ns_index::DocInfo* doc=index->GetForwardIndex(item.doc_id);
                if(nullptr==doc)
                {
                    continue;
                }
                Json::Value elem;
                elem["title"]=doc->title;
                elem["desc"]=GetDesc(doc->content,item.words[0]);//content是去标签的结果，但是不是我们想要的，我们想要的只是content的一部分 TODO
                elem["url"]=doc->url;

                //for debug
                // elem["weight"]=item.weight;
                // elem["id"]=doc->doc_id;


                root.append(elem);
            }
            Json::StyledWriter writer;
            json_string=writer.write(root);
        }
        std::string GetDesc(const std::string& html_content,const std::string word)
        {
            //找到word在html_content中的首次出现，然后往前找50字节(如果没有，从begin开始)，往后找100字节(如果没有，到end就可以的)
            //截取出这部分内容
            const int prev_step=50;
            const int next_step=100;
            //1.找到首次出现
            // const int pos=html_string.find(word);
            // if(pos==std::string::npos)
            //     return "None1";//不可能出现这种情况 //大小写问题可以出现：文档是Split，但是关键字是split。导致找不到位置
            auto iter=search(html_content.begin(),html_content.end(),word.begin(),word.end(),[](int x,int y){
                return (std::tolower(x)==std::tolower(y));
            });
            int pos=std::distance(html_content.begin(),iter);

            //2.获取start,end
            int start=0;
            int end=html_content.size()-1;
            if(pos-prev_step>start)start=pos-prev_step;
            if(pos+next_step<end)end=pos+next_step;
            
            //3.截取子串
            if(start>end)return "None2";
            std::string desc= html_content.substr(start,end-start);
            desc+="...";
            return desc;
        }
    };
}